package lib;

import java.io.File;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

import rmi.ClientRmiInterface;
import rmi.Groupe;
import rmi.Log;
import rmi.Message;
import client.Client;

public class Abcast extends Protocole{

	private HashMap<Integer ,Client> listClient;
	
	/**
	 * Constructeur.
	 * 
	 * @param listClient
	 * 					la liste de client
	 */
	public Abcast(HashMap<Integer ,Client> listClient){
		this.listClient = listClient;
	}
	
	/**
	 * Le methode pour envoyer un message
	 * @param msg Message
	 */
	public synchronized void envMsg(Message msg) {
		
		Client c = listClient.get(msg.getId());
		int estampille = c.getEstampille().getEstampille();
		estampille++;
		c.getEstampille().setEstampille(estampille);
		msg.setDate(c.getEstampille());
	}
	
    public void lancerThread(Message msg,File logfile,int flag,int length,ArrayList<Integer> list)
    {
      if(flag==0)	
      {
    	  Log log=new Log(null,Log.Action.emmi,msg);
    	  envMsg(msg);
    	  Receiver r=new Receiver(msg,listClient,logfile);
    	  r.start();
    	  System.out.println("thread start");
    	  try {
    		  r.join();
    	  } catch (InterruptedException e) {
			
    		  e.printStackTrace();
    	  }
      }
      else if(flag==1)
      {
    	CyclicBarrier barrier =new CyclicBarrier(length);
  		  Receiversf rsfs[]=new Receiversf[length];
  		
  		for(int i=0;i<length;i++)
  		{
  			rsfs[i]=new Receiversf(barrier,list,msg);
  			
  		}
  		for(Receiversf rsf:rsfs)
  		{
  			rsf.start();
  		}
  		for (Receiversf s :rsfs) {
  			try {
  				s.join();
  			} catch (InterruptedException e) {
  				e.printStackTrace();
  			}
  		}
      }
    }
 
    /**
	 * retourner la liste de messages
	 * @param msg 
	 * 				Message recu
	 * @param aProc
	 * 				L'identificateur de client qui recieve le message
	 * @return retourner la liste de messages recu pas le client 
	 */
    public synchronized  Message recuMsgs(Message msg,int id) {
		Client c = listClient.get(id);
		if(msg.getDate().getEstampille()>c.getEstampille().getEstampille()){
			c.setEstampille(msg.getDate());
		}else if(msg.getDate().getEstampille()==c.getEstampille().getEstampille())
		{
			int est=msg.getDate().getEstampille()+1;
			msg.getDate().setEstampille(est);
			c.setEstampille(msg.getDate());
		}
		else{
			msg.setDate(c.getEstampille());
		}
		return msg;
	}
    
	/*
	 * classe thread Receiver c'est une classe heritage 
	 * du classe thread pour le Abcast
	 * Reçu le message et recuperer la liste de 
	 * client doit envoyer et invoquer thread 
	 * sender pour envoyer ses messages à
	 * ses client
	 */
	static class Receiver extends Thread
	{
		
		private final Message msg;
		private HashMap<Integer ,Client> listClient;
		private File logfile;
		private CyclicBarrier barrier;
		
		public CyclicBarrier getBarrier() {
			return barrier;
		}
		public Receiver(Message msg,HashMap<Integer ,Client> listClient,File logfile)
		{
			
			this.msg=msg;
			this.listClient=listClient;
			this.logfile=logfile;
		}
		@Override
		public void run() {
			try {
				/*
				 * faire tirage aléatoire du temps à attendre avant de lui faire suivre le message
				 */
				sleep((int) (Math.floor(Math.random() *Protocole.SLEEPTIMES)));
				
				int[] list = Groupe.getListDelivrance(msg.getId(),listClient);
				Sender senders[]=new Sender[list.length]; 
				barrier=new CyclicBarrier(list.length,new Runnable() {
					 public void run() {
						
						System.out.println("synchronize envois message");
					}
				});
				Log log=new Log(list,Log.Action.delivr,msg);
				System.out.println(log.getLogText());
				int k=0;
				Groupe.writelog(log.getLogText(),logfile);
				for(Integer i:list)
				{
					/*
					* créer les threads pour sender message
					*/
					senders[k]=new Sender(i, msg,getBarrier());
					k++;
				}
				/*
				 * demarrer tous les thread
				 */
				for (int i = 0; i < senders.length; i++) {
					senders[i].start();
				}
				/*
				 * attendre tous les thread est fini
				 */
				for (Sender s :senders) {
					try {
						s.join();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}	
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/*
	 * classe Receiver c'est une classe heritage du classe thread
	 * qui correspond les action pour quand groupe reçu le message
	 * avec flag egale à 1
	 */
	static class Receiversf extends Thread
	{
		private CyclicBarrier barrier;
		private ArrayList<Integer> list;
		private Message msg;
		public Receiversf(CyclicBarrier barrier,ArrayList<Integer> list,Message msg)
		{
			this.barrier=barrier;
			this.list=list;
			this.msg=msg;
		}
	
		public void run() {
			list.add(msg.getDate().getEstampille());
			try {
				barrier.await();
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				
				e.printStackTrace();
			}
			
		}
	}
	
	/*classe Sender  c'est une classe heritage du classe thread
	 * pour send message à client correspondant 
	 */
	static class Sender extends Thread
	{
		private int idcl;//id du client
		private Message msg;
		private CyclicBarrier barrier;
		public Sender(int idcl,Message msg,CyclicBarrier barrier)
		{
			this.idcl=idcl;
			this.msg=msg;
			this.barrier=barrier;
		}
		public void run() {
			ClientRmiInterface serv=Groupe.initContext(idcl);
			try {
				
				serv.receiveMsg(msg);
				barrier.await();
				 
			}catch (RemoteException e) {
				e.printStackTrace();
			}catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
	    }
		
	}
	
	public ArrayList<Message> recuMsg(Message msg, int aProc) {
		return null;
	}

	
	public void init(int compteur) {
	}

}
